package cn.virens.server.netty_udp;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.virens.server.netty_udp.exception.HandlerException;
import cn.virens.server.netty_udp.handler.PacketHandler;
import cn.virens.server.netty_udp.packet.UdpReadPacket;
import cn.virens.server.netty_udp.packet.UdpWritePacket;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;

public class UDPServerPacketHandler extends SimpleChannelInboundHandler<UdpReadPacket> {
	private final Logger logger = LoggerFactory.getLogger(UDPServerPacketHandler.class);

	private final Map<String, UDPServerChannel> channelPool = new ConcurrentHashMap<>();
	private final List<PacketHandler> handlerPool = new ArrayList<>();

	private ChannelHandlerContext context;

	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		this.context = ctx;
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		if (cause instanceof HandlerException) {
			logger.error("处理异常.Code: " + ((HandlerException) cause).getCode());
			logger.error("处理异常.Message: " + ((HandlerException) cause).getMessage());
			logger.error("处理异常.Throwable: ", cause);
		} else {
			super.exceptionCaught(ctx, cause);
		}
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, UdpReadPacket packet) throws Exception {
		for (PacketHandler handler : handlerPool) {
			if (handler.can(packet.getCommand())) {
				handler.handle(packet);
			}
		}
	}

	protected UDPServerChannel poolRm(String mac) {
		UDPServerChannel uDPServerChannel = channelPool.remove(mac);
		if (uDPServerChannel == null) return null;

		//  TODO 设备连接下线，发下线消息

		return uDPServerChannel;
	}

	protected UDPServerChannel poolGet(String mac) {
		return channelPool.get(mac);
	}

	protected void poolEach(Consumer<UDPServerChannel> action) {
		for (UDPServerChannel uDPServerChannel : channelPool.values()) {
			action.accept(uDPServerChannel);
		}
	}

	protected boolean write(UdpWritePacket packet) {
		if (context == null) return false;
		if (packet == null) return false;

		try {
			context.writeAndFlush(packet);
		} catch (Exception e) {
			return false;
		}

		return true;
	}

	protected UDPServerChannel warp(String mac, DatagramPacket packet) {
		UDPServerChannel answer = channelPool.computeIfAbsent(mac, (m) -> {
			// TODO 设备连接上线，发上线消息

			return new UDPServerChannel(mac);
		});

		// 更新连接对象的心跳数据
		answer.heatTimes().incrementAndGet();
		answer.heatIdle().getAndSet(0);

		return answer.warp(packet.sender());
	}

	protected void addHandler(PacketHandler handler) {
		this.handlerPool.add(handler);
	}
}
